package work.thread_eg.thread0.safe;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.openjdk.jol.vm.VM;
import work.entity.User;

/**
 * 买票问题
 *
 * 关于线程安全的问题
 *      1 当多个线程操作共享资源时 才会出现线程安全问题
 *              (共享资源可以是实现了Runnable接口的对象 也就是相同的内存空间 可以是个基本类型数据)
 *              这边的共享资源就是Ticket对象 实际上就是对象的allTickets这个基本数据类型的变量
 *        如果多个线程操作的是独立的资源 各个线程之间不会相互影响
 *      2 对共享资源的判断 主要依据是否占用相同的内存空间
 *          注意下引用类型的变量实际上只是地址值 需要查看传入的变量对应的实际内存空间是否相同 才能判断是否存在共享资源
 *          参考 https://blog.csdn.net/lizefeng1998/article/details/120395481 取钱问题
 *      3 线程私有
 *          run()方法内部声明的内存空间(new的对象)是线程私有的 每个new都是新开辟内存空间
 *
 * 关于线程的写法
 *      线程的run()是没有参数的 一般业务逻辑会使用一个业务类实现Runnable接口
 *      然后run()方法需要的变量放在业务类的成员变量中
 *
 */
public class ThreadSafe {

    public static void main(String[] args) {
        ThreadSafe threadSafe = new ThreadSafe();
        threadSafe.work();
    }

    public void work() {
        // 这样子 每个线程用于自己独立的对象资源 彼此不会干扰 每个人都是买了20张票
//        new Thread(new Ticket(), "小明同学").start();
//        new Thread(new Ticket(), "小华同学").start();

        // 多个线程操作相同的资源 同一个Ticket对象 应该一共卖出20张票才对
        // 这个时候就会出现线程安全问题  会出现重复票和顺序错乱的问题
        Ticket t = new Ticket();
//        Ticket t = new Ticket(5,true);
        new Thread(t , "小明同学").start();
        new Thread(t , "小华同学").start();
        new Thread(t , "黄牛").start();
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    class Ticket implements Runnable {
        /**
         * 票数
         */
        private int allTickets = 20;

        private boolean flag = true;

        @Override
        public void run() {
            User user = new User();
            System.out.println(VM.current().addressOf(user));
            while (allTickets > 0) {
                try {
                    Thread.sleep(100);//扩大时长，模拟实际情况
                    buy();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        public void buy() throws InterruptedException {
            if (this.allTickets <= 0) {
                System.out.println(Thread.currentThread().getName() + "没票可买了");
                this.flag = false;
                return;
            } else {
                System.out.println(Thread.currentThread().getName() + "买了第" + this.allTickets-- + "张票   ");
            }
        }
    }

}
